package com.atguigu.juc.day01.create;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * API 介绍：
 * 1、Callable接口：java.util.concurrent.Callable
 *      JUC 包下的一个创建多线程的新方式
 * 2、FutureTask类：java.util.concurrent.FutureTask
 *      FutureTask 类间接实现了两个接口：
 *          Future接口：定义了 FutureTask 类的主要功能
 *          Runnable接口：让 Callable 能够兼容 Thread
 * 3、Future接口
 *      get()：获取目标任务结果，在获取过程中会一直等待，直到目标任务返回；目标任务返回前会阻塞调用 get() 方法的当前线程
 *      cancel()：尝试取消当前线程，如果取消成功返回 true，否则返回 false。
 *          取消失败通常是因为任务已经正常结束
 *      isDone()：判断当前任务是否已完成
 *      isCancelled()：判断当前任务是否已取消
 *      get(time, timeUnit)：获取目标任务结果，最多等待指定时间
 *
 * 操作方式：
 * 1、创建 Callable 类型的对象：把新线程要执行的操作封装起来
 * 2、把 Callable 类型的对象传入 FutureTask 构造器
 * 3、把 FutureTask 对象传给 Thread 构造器
 * 4、调用 Thread 对象的 start() 启动新线程
 * 5、调用 FutureTask 对象的 get() 方法获取线程执行结果
 *
 * 实际意义：把串行的任务拆分成并行的任务
 * 同样的五个任务：
 *      A 任务：5s
 *      B 任务：3s
 *      C 任务：8s
 *      D 任务：7s
 *      E 任务：2s
 * 串行执行耗时：5 + 3 + 8 + 7 + 2 = 25s
 * 并行执行耗时：取最长时间 8s
 * 前提：CPU 有多个或保护多个核心
 */
public class Demo03CallableFutureTask {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1、创建 Callable 类型的对象：把新线程要执行的操作封装起来
        Callable<String> callable01 = () -> {

            System.out.println(Thread.currentThread().getName() + " is working.");

            TimeUnit.SECONDS.sleep(1);

            return "task 01 result";
        };
        Callable<String> callable02 = () -> {

            System.out.println(Thread.currentThread().getName() + " is working.");

            TimeUnit.SECONDS.sleep(2);

            return "task 02 result";
        };
        Callable<String> callable03 = () -> {

            System.out.println(Thread.currentThread().getName() + " is working.");

            TimeUnit.SECONDS.sleep(3);

            return "task 03 result";
        };

        // 2、把 Callable 类型的对象传入 FutureTask 构造器
        FutureTask<String> futureTask01 = new FutureTask<>(callable01);
        FutureTask<String> futureTask02 = new FutureTask<>(callable02);
        FutureTask<String> futureTask03 = new FutureTask<>(callable03);

        // 3、把 FutureTask 对象传给 Thread 构造器
        Thread thread01 = new Thread(futureTask01);
        Thread thread02 = new Thread(futureTask02);
        Thread thread03 = new Thread(futureTask03);

        // 4、调用 Thread 对象的 start() 启动新线程
        thread01.start();
        thread02.start();
        thread03.start();

        // 5、调用 FutureTask 对象的 get() 方法获取线程执行结果
        // ※ get() 方法的说明：在尚未拿到目标任务的结果时，会阻塞当前所在线程
        // [1]目标任务没有 return 的时候，get()方法就会一直处于等待状态
        // [2]目标任务执行 return，get()方法拿到任务结果返回
        // [3] get()方法是主线程调用的
        // [4]所以 get()方法在等待目标任务 return 的时候，主线程也被阻塞了
        // [5]同一个线程内部，前面操作不执行完，后面操作就只能等着
        String task01Result = futureTask01.get();
        System.out.println("task01Result = " + task01Result);

        String task02Result = futureTask02.get();
        System.out.println("task02Result = " + task02Result);

        String task03Result = futureTask03.get();
        System.out.println("task03Result = " + task03Result);
    }

}
